home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / interapplication comm / 7edit / source / svdrag.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  26.7 KB  |  1,123 lines

  1. /*
  2.     File:        SVDrag.c
  3.  
  4.     Contains:    
  5.  
  6.     Written by: Original version by Jon Lansdell and Nigel Humphreys.
  7.                             3.1 updates by Greg Sutton.    
  8.  
  9.     Copyright:    Copyright ©1995-1999 by Apple Computer, Inc., All Rights Reserved.
  10.  
  11.                 You may incorporate this Apple sample source code into your program(s) without
  12.                 restriction. This Apple sample source code has been provided "AS IS" and the
  13.                 responsibility for its operation is yours. You are not permitted to redistribute
  14.                 this Apple sample source code as "Apple sample source code" after having made
  15.                 changes. If you're going to re-distribute the source, we require that you make
  16.                 it clear in the source that the code was descended from Apple sample source
  17.                 code, but that you've made changes.
  18.  
  19.     Change History (most recent first):
  20.                 7/19/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  21.                 
  22.  
  23. */
  24. #include "SVDrag.h"
  25. #include "SVEditWindow.h"
  26. #include "SVEditGlobals.h"
  27. #include "Offscreen.h"
  28.  
  29. #include "DebugUtils.h"
  30.  
  31.  
  32. #include <Drag.h>
  33. #include <LowMem.h>
  34. #include <Errors.h>
  35. #include <Folders.h>
  36.  
  37.  
  38.  
  39. static short    gCaretOffset;        // Caret drawn during a drag
  40.  
  41.  
  42.  
  43. #pragma segment Drag
  44.  
  45. //
  46. // InitDragHandlers creates the UPPs for the Drag Manager 
  47. // callback routines _if_ the Drag Manager is available.
  48. //
  49. OSErr InitDragHandlers ( void )
  50. {
  51.     OSErr    theErr = noErr;
  52.     
  53.     if ( gHasDragManager )
  54.     {
  55.         gDragTrackingHandlerUPP = NewDragTrackingHandlerProc ( MyTrackingHandler );
  56.         gDragReceiveHandlerUPP = NewDragReceiveHandlerProc ( MyReceiveHandler );
  57.     }
  58.     
  59.     return theErr;
  60. }
  61.  
  62.  
  63.  
  64. //
  65. // InstallDragHandlers attaches the tracking and receive handlers to
  66. // one of the application's windows.
  67. //
  68. OSErr InstallDragHandlers ( WindowRef theWindow )
  69. {    
  70.     OSErr     theErr = noErr;
  71.     
  72.     if ( gHasDragManager )
  73.     {
  74.         
  75.         theErr = InstallTrackingHandler ( gDragTrackingHandlerUPP, theWindow, nil );
  76.     
  77.         if ( theErr == noErr )
  78.         {
  79.             theErr = InstallReceiveHandler ( gDragReceiveHandlerUPP, theWindow, nil );
  80.             if ( theErr )
  81.                 (void) RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow );
  82.         }
  83.         
  84.     }
  85.     
  86.     return theErr;
  87. }
  88.  
  89.  
  90.  
  91. //
  92. // RemoveDragHandlers removes the tracking and receive handlers from
  93. // one of the application's windows (usually just prior to disposal).
  94. //
  95. void RemoveDragHandlers ( WindowRef theWindow )
  96. {
  97.     if ( gHasDragManager )
  98.     {
  99.         
  100.         RemoveReceiveHandler ( gDragReceiveHandlerUPP, theWindow );
  101.         RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow );
  102.         
  103.     }
  104.     
  105.     return;
  106. }
  107.  
  108.  
  109.  
  110. Boolean IsDragInWindowContent ( DragReference theDrag, WindowRef theWindow )
  111. {
  112.     Point    localPt;
  113.     
  114.     GetDragMouse ( theDrag, &localPt, 0L );
  115.     GlobalToLocal ( &localPt );            // Assumes theWindow is current port
  116.     
  117.     return PtInWindow ( localPt, theWindow );
  118. }
  119.  
  120.  
  121.  
  122. Boolean PtInWindow ( Point localPt, WindowRef theWindow )
  123. {
  124.     DPtr theDocument;
  125.     
  126.     theDocument = DPtrFromWindowPtr ( theWindow );
  127.     return PtInDocument ( localPt, theDocument );
  128. }
  129.  
  130.  
  131.  
  132. Boolean PtInDocument ( Point localPt, DPtr theDocument )
  133. {
  134.     return PtInRect ( localPt, &(**(theDocument->theText)).viewRect );
  135. }
  136.  
  137.  
  138.  
  139. Boolean CanAcceptDragItems ( DragReference theDrag )
  140. {
  141.     OSErr            theErr;
  142.     ItemReference    theRef;
  143.     Boolean            bCanAccept = false;
  144.     HFSFlavor         currHFSFlavor;
  145.     Size            flavorDataSize;
  146.     FlavorFlags        currFlavorFlags;
  147.     
  148.     
  149.     
  150.     theErr = GetDragItemReferenceNumber ( theDrag, 1, &theRef );
  151.     if ( theErr == noErr )
  152.     {
  153.         // use GetFlavorFlags to check on flavor existence of TEXT data.
  154.         theErr = GetFlavorFlags ( theDrag, theRef, 'TEXT', &currFlavorFlags );
  155.         if ( theErr == noErr )
  156.             bCanAccept = true;
  157.         else
  158.         {
  159.             // check if the item is a file spec, and it contains TEXT
  160.             flavorDataSize = sizeof ( HFSFlavor );
  161.             theErr = GetFlavorData ( theDrag, theRef, flavorTypeHFS, &currHFSFlavor,
  162.                                             &flavorDataSize, 0    );
  163.             
  164.             if ( theErr == noErr && currHFSFlavor.fileType == 'TEXT' ) 
  165.                 bCanAccept = true;
  166.         }
  167.     }
  168.     
  169.     return bCanAccept;
  170. }
  171.  
  172.  
  173.  
  174. pascal OSErr MyTrackingHandler ( DragTrackingMessage theMessage, WindowPtr theWindow,
  175.                                     void* handlerRefCon, DragReference theDrag )
  176. {
  177. #pragma unused(handlerRefCon)
  178.  
  179.     static Boolean    bHasAcceptableDrag;
  180.     static Boolean    bHasHilitedWindow;
  181.     static Boolean    bShowCaret;
  182.     
  183.     static long        caretMovedTime;
  184.     static short    lastOffset, insertPosition;
  185.     
  186.     short            theOffset;
  187.     OSErr            theErr = noErr;
  188.     long            theTime = TickCount ( );
  189.     unsigned long    theAttributes;
  190.     RgnHandle        tempRgn;
  191.     DPtr            theDocument, hitDocument;
  192.     
  193.     
  194.     GetDragAttributes ( theDrag, &theAttributes );
  195.     theDocument = DPtrFromWindowPtr ( theWindow );
  196.     
  197.     switch ( theMessage )
  198.     {
  199.         case kDragTrackingEnterHandler:
  200.             // Any initialization for this window handler.
  201.             bHasAcceptableDrag = CanAcceptDragItems ( theDrag );
  202.             
  203.             // Let the drag manager know if we can't accept this drag
  204.             if ( !bHasAcceptableDrag )
  205.                 theErr = dragNotAcceptedErr;
  206.         break;
  207.             
  208.         case kDragTrackingEnterWindow: 
  209.             
  210.             caretMovedTime = theTime;
  211.             gCaretOffset = lastOffset = -1;
  212.             bShowCaret = true;
  213.  
  214.             bHasHilitedWindow = false;
  215.             
  216.         break;
  217.         
  218.         case kDragTrackingInWindow:
  219.             // Hiliting of the window during a drag is done
  220.             // here.  Do it only if we can accept these items
  221.             // and we're not in the source window.
  222.             
  223.             if ( bHasAcceptableDrag )
  224.             {
  225.                 // Check if the mouse is in a window's content region. Make an
  226.                 // exception if the window has yet to leave the source window,
  227.                 // since we don't want to hilite it in that stuation.
  228.                 Boolean        bMouseInContent;
  229.                 Point        theMouse;
  230.                 
  231.                 bMouseInContent = false;
  232.                 if ( theAttributes & kDragHasLeftSenderWindow )
  233.                     bMouseInContent = IsDragInWindowContent ( theDrag, theWindow );
  234.                 
  235.                 // If the mouse is in a window and it isn't hilited...
  236.                 if ( bMouseInContent && !bHasHilitedWindow )
  237.                 {
  238.                     tempRgn = NewRgn ( );
  239.                     RectRgn ( tempRgn, &(**(theDocument->theText)).viewRect );
  240.                     
  241.                     // ...draw the hilight...
  242.                     if ( ShowDragHilite ( theDrag, tempRgn, true ) == noErr )
  243.                         // ... and remember it's now hilited
  244.                         bHasHilitedWindow = true;
  245.                     
  246.                     DisposeRgn ( tempRgn );
  247.                 }
  248.                 
  249.                 
  250.                 GetDragMouse ( theDrag, &theMouse, 0L );
  251.                 theOffset = HitTest ( theMouse, &hitDocument );
  252.                 
  253.                 if ( theDocument == hitDocument )
  254.                 {
  255.                 
  256.                     // Do not allow tracking through the selection in the
  257.                     // window that sourced the drag.
  258.                     if ( theAttributes & kDragInsideSenderWindow )
  259.                         if ( IsOffsetInSelection ( theOffset, theDocument->theText ) )
  260.                             theOffset = -1;
  261.                         
  262.                     
  263.                     
  264.                     insertPosition = theOffset;
  265.                     
  266.                     //    Reset flashing counter if the offset has moved. This makes the
  267.                     //    caret blink only after the caret has stopped moving long enough.
  268.                     if ( theOffset != lastOffset )
  269.                     {
  270.                         caretMovedTime = theTime;
  271.                         bShowCaret = true;
  272.                     }
  273.                     lastOffset = theOffset;
  274.                     
  275.                     
  276.                     //    Flash caret.
  277.                     if ( theTime - caretMovedTime > LMGetCaretTime ( ) )
  278.                     {
  279.                         bShowCaret = !bShowCaret;
  280.                         caretMovedTime = theTime;
  281.                     }
  282.                     
  283.                     if ( !bShowCaret )
  284.                         theOffset = -1;
  285.                         
  286.                     //    If caret offset has changed, move caret on screen.
  287.                     if (theOffset != gCaretOffset)
  288.                     {
  289.                         if (gCaretOffset != -1)
  290.                             DrawCaret(gCaretOffset, theDocument->theText);
  291.                         
  292.                         if (theOffset != -1)
  293.                             DrawCaret(theOffset, theDocument->theText);
  294.                     }
  295.                     
  296.                     gCaretOffset = theOffset;
  297.                 }
  298.                 else
  299.                 {
  300.                     lastOffset = theOffset;
  301.                     insertPosition = -1;
  302.                 }
  303.  
  304.             }
  305.         break;
  306.         
  307.         case kDragTrackingLeaveWindow:
  308.             //    If the caret is on the screen, remove it.
  309.             if (gCaretOffset != -1)
  310.             {
  311.                 DrawCaret(gCaretOffset, theDocument->theText);
  312.                 gCaretOffset = -1;
  313.             }
  314.             
  315.             // Remove any window hiliting here. If the mouse
  316.             // is not in the window and it's hilited...
  317.             if ( bHasHilitedWindow )
  318.                 // ...erase the hilight...
  319.                 if ( HideDragHilite ( theDrag ) == noErr )
  320.                     // ...remember that nothing is hilited
  321.                     bHasHilitedWindow = false;
  322.         break;
  323.         
  324.         case kDragTrackingLeaveHandler:
  325.         break;
  326.         
  327.         default:
  328.             theErr = paramErr;
  329.         break;
  330.     }
  331.     
  332.     return theErr;
  333. }
  334.  
  335.  
  336.  
  337. Boolean IsOffsetInSelection ( short theOffset, TEHandle theTE )
  338. {
  339.     return (theOffset >= (*theTE)->selStart && theOffset <= (*theTE)->selEnd);
  340. }
  341.  
  342.  
  343.  
  344. //
  345. // DragIsNotInSourceWindow returns true if the drag in progress
  346. // is not in the same window it originated in. This is called by
  347. // the tracking and receive handlers.
  348. //
  349. static Boolean DragIsNotInSourceWindow ( DragReference theDrag )
  350. {
  351.     DragAttributes currDragFlags;
  352.     
  353.     GetDragAttributes ( theDrag, &currDragFlags );
  354.     return !(currDragFlags & kDragInsideSenderWindow);
  355. }
  356.  
  357.  
  358.  
  359. //
  360. // DragReceiver is called by the drag manager whenever an
  361. // item is dropped on one of the application's windows.
  362. //
  363. pascal OSErr MyReceiveHandler ( WindowPtr theWindow, void* handlerRefCon, 
  364.                                     DragReference theDrag )
  365. {
  366. #pragma unused(handlerRefCon)
  367.  
  368.     Boolean                bHaveData = false;
  369.     Boolean                bMove = false;
  370.     OSErr                theErr = noErr;
  371.     
  372.     
  373.     unsigned short        numItems;
  374.     int                    i;
  375.     unsigned long        theAttributes;
  376.     ItemReference        theItem;
  377.     Size                textSize, styleSize;
  378.     DPtr                theDocument;
  379.     
  380.     short                theOffset;
  381.     Point                thePoint = { 0, 0 };
  382.     
  383.     Ptr                    textData = nil;
  384.     StScrpHandle        styleHandle = nil;
  385.  
  386.     TEHandle            tempTE = nil;
  387.     Rect                tempRect;
  388.  
  389.     
  390.     
  391.     RgnHandle            tempRgn;
  392.     Rect                sourceRect, targetRect;
  393.  
  394.     tWindowOffscreen*    theOffscreen = nil;
  395.     
  396.     
  397.     
  398.     
  399.     if ( !(CanAcceptDragItems ( theDrag ) && IsDragInWindowContent ( theDrag, theWindow )) )
  400.         return dragNotAcceptedErr;
  401.     
  402.     theDocument = DPtrFromWindowPtr ( theWindow );
  403.     GetDragAttributes ( theDrag, &theAttributes );
  404.     
  405.     // Get the location of the drop
  406.     theErr = GetDragMouse ( theDrag, &thePoint, 0L );
  407.     if ( theErr ) goto CleanupAndBail;
  408.     
  409.     GlobalToLocal ( &thePoint );            // Assumes theWindow is current port
  410.     // Map the drop location to the text
  411.     theOffset = TEGetOffset ( thePoint, theDocument->theText );
  412.  
  413.     // Don't allow a drop within the original selection
  414.     if ( theAttributes & kDragInsideSenderWindow )
  415.         if ( IsOffsetInSelection ( theOffset, theDocument->theText ) )
  416.             return dragNotAcceptedErr;
  417.             
  418.     
  419.     // Is this drag copying or a moving the text?
  420.     bMove = IsDragMoving ( theDrag );
  421.     
  422.     // First, gather all the text into a temp TE record, and then insert that into
  423.     // the doucment. This approach makes it easy to handle things like text selection.
  424.     SetRect ( &tempRect, 0, 0, 0, 0 );
  425.     tempTE = TEStyleNew ( &tempRect, &tempRect );
  426.     theErr = MemError ( );
  427.     if ( tempTE == nil || theErr ) goto CleanupAndBail;
  428.     
  429.     theErr = CountDragItems ( theDrag, &numItems );
  430.     if ( theErr ) goto CleanupAndBail;
  431.     
  432.     for ( i = 1; i <= numItems; i++ )
  433.     {
  434.         
  435.         theErr = GetDragItemReferenceNumber ( theDrag, i, &theItem );
  436.         if ( theErr ) goto CleanupAndBail;
  437.         
  438.         theErr = GetFlavorDataSize ( theDrag, theItem, 'TEXT', &textSize );
  439.         if ( theErr ) goto CleanupAndBail;
  440.         
  441.         textData = NewPtr ( textSize );
  442.         theErr = MemError ( );
  443.         if ( textData == nil || theErr ) goto CleanupAndBail;
  444.         
  445.         theErr = GetFlavorData ( theDrag, theItem, 'TEXT', textData, &textSize, 0L );
  446.         if ( theErr ) goto CleanupAndBail;
  447.         
  448.         
  449.         // Check for optional styl data for the TEXT.
  450.         styleHandle = 0L;
  451.         theErr = GetFlavorDataSize ( theDrag, theItem, 'styl', &styleSize );
  452.         if ( !theErr )
  453.         {
  454.             styleHandle = (StScrpHandle) NewHandle ( styleSize );
  455.             theErr = MemError ( );
  456.             if ( styleHandle == nil || theErr ) goto CleanupAndBail;
  457.             
  458.             HLock ( (Handle) styleHandle );
  459.             theErr = GetFlavorData ( theDrag, theItem, 'styl', *styleHandle, &styleSize, 0L );
  460.             if ( theErr ) goto CleanupAndBail;
  461.             HUnlock ( (Handle) styleHandle );
  462.         }
  463.         
  464.         // Insert this drag item's text into the tempTE.
  465.         TESetSelect ( 32767, 32767, tempTE );
  466.         TEStyleInsert ( textData, textSize, styleHandle, tempTE );
  467.         
  468.         DisposePtr ( textData );
  469.         textData = nil;
  470.         if ( styleHandle )
  471.         {
  472.             DisposeHandle ( (Handle) styleHandle );
  473.             styleHandle = nil;
  474.         }
  475.         
  476.     }
  477.     
  478.     
  479.     // Pull the TEXT and styl data out of the tempTE handle.
  480.     textData = NewPtr (textSize = (**tempTE).teLength );
  481.     theErr = MemError ( );
  482.     if ( textData == nil || theErr ) goto CleanupAndBail;
  483.     
  484.     BlockMoveData ( *(*tempTE)->hText, textData, textSize );
  485.     TESetSelect ( 0, 32767, tempTE );
  486.     styleHandle = TEGetStyleScrapHandle (tempTE );
  487.     TEDispose ( tempTE );
  488.     tempTE = nil;
  489.     
  490.     
  491.     // Insert any text into the destination.
  492.     if ( textSize != 0 )
  493.     {
  494.         
  495.         // Get rid of the hilite/caret before inserting
  496.         if ( theAttributes & kDragHasLeftSenderWindow )
  497.             HideDragHilite ( theDrag );
  498.         
  499.         if ( gCaretOffset != -1 )
  500.         {
  501.             DrawCaret ( gCaretOffset, theDocument->theText );
  502.             gCaretOffset = -1;
  503.         }
  504.         
  505.         //    If the drag occurred completely within the same window and the window is not
  506.         //    frontmost, bring the window forward and update its contents before completing
  507.         //    the drag.
  508.         if ( (theAttributes & kDragInsideSenderWindow) && (theWindow != FrontWindow ( )) )
  509.         {
  510.             SelectWindow ( theWindow );
  511.             DoUpdate ( theWindow );
  512.             TEActivate ( theDocument->theText );
  513.         }
  514.         
  515.         //    If the window is not active, must activate TE before inserting
  516.         //    text or the background hilite will not update correctly.
  517.         if ( !IsWindowHilited ( theWindow ) )
  518.             TEActivate ( theDocument->theText );
  519.         
  520.         //    Draw everything into offscreen pixmap.
  521.         theOffscreen = DrawOffscreen ( theWindow );
  522.         if ( theOffscreen  )
  523.             (*theDocument->theText)->inPort = (GrafPtr) theOffscreen->offscreenWorld;
  524.         
  525.         if ( bMove )
  526.         {
  527.             // Get the current hilite rgn for zooming (source)
  528.             tempRgn = NewRgn ( );
  529.             theErr = MemError ( );
  530.             if ( theErr )    goto CleanupAndBail;
  531.             GetSelectedTextRgn ( theDocument, tempRgn );
  532.             sourceRect = (*tempRgn)->rgnBBox;
  533.             LocalRectToGlobalRect ( &sourceRect, theWindow );
  534.             
  535.             // If this is a move operation, delete the old text.
  536.             DeleteTextSelection ( theDocument->theText, &theOffset );
  537.         }
  538.         
  539.         InsertTextAtOffset ( theOffset, textData, textSize, styleHandle, theDocument->theText );
  540.         
  541.         // If the text is moving (not copying) within the same window, provide a ZoomRects
  542.         // from the source to the destination before revealing the reflowed text.
  543.         
  544.         if ( bMove )
  545.         {
  546.             // Get the current hilite rgn for zooming (target)
  547.             GetSelectedTextRgn ( theDocument, tempRgn );
  548.             targetRect = (*tempRgn)->rgnBBox;
  549.             DisposeRgn ( tempRgn );
  550.             tempRgn = nil;
  551.             LocalRectToGlobalRect ( &targetRect, theWindow );
  552.             
  553.             ZoomRects ( &sourceRect, &targetRect, 12, kZoomDecelerate );
  554.         }
  555.     }
  556.     
  557.     // Undo the TEActivate, if needed.
  558.     if ( !IsWindowHilited ( theWindow ) )
  559.         TEDeactivate ( theDocument->theText );
  560.     
  561.     //    Show the offscreen bitmap.
  562.     if ( theOffscreen )
  563.     {
  564.         theOffscreen = DrawOnscreen ( theOffscreen );
  565.         (*theDocument->theText)->inPort = theWindow;
  566.     }
  567.     
  568.     // Make the document dirty
  569.     theDocument->dirty = true;
  570.     
  571.     
  572. CleanupAndBail:
  573.     
  574.     
  575.     // All of these will be nil if no error occurred
  576.     if ( textData )
  577.         DisposePtr ( textData );
  578.     if ( styleHandle )
  579.         DisposeHandle ( (Handle) styleHandle );
  580.     if ( tempTE )
  581.         TEDispose ( tempTE );
  582.     if ( tempRgn )
  583.         DisposeRgn ( tempRgn );
  584.     
  585.     // Normally  nil since DrawOnscreen calls DisposeOffscreen
  586.     if ( theOffscreen )
  587.     {
  588.         DisposeOffscreen ( theOffscreen );
  589.         (*theDocument->theText)->inPort = theWindow;
  590.     }
  591.     
  592.     
  593.     return theErr;
  594. }
  595.  
  596.  
  597.  
  598. void DeleteTextSelection ( TEHandle theTE, short* theInsertPosition )
  599. {
  600.     short selStart, selEnd;
  601.     
  602.     selStart = (*theTE)->selStart;
  603.     selEnd = (*theTE)->selEnd;
  604.     if ( WhiteSpaceAtOffset ( selStart - 1, theTE ) &&
  605.             !WhiteSpaceAtOffset ( selStart, theTE ) &&
  606.             !WhiteSpaceAtOffset ( selEnd - 1, theTE ) &&
  607.              WhiteSpaceAtOffset ( selEnd, theTE ) )
  608.     {
  609.         
  610.         if ( GetCharAtOffset ( selEnd, theTE ) == ' ' )
  611.             (*theTE)->selEnd++;
  612.     }
  613.     
  614.     if ( *theInsertPosition > selStart )
  615.         *theInsertPosition -= ((*theTE)->selEnd - (*theTE)->selStart);
  616.     
  617.     TEDelete ( theTE );
  618.     
  619.     return;
  620. }
  621.  
  622.  
  623.  
  624. static Boolean IsDragMoving ( DragReference theDrag )
  625. {
  626.     DragAttributes    theAttributes;
  627.     
  628.     GetDragAttributes ( theDrag, &theAttributes );
  629.     return (theAttributes & kDragInsideSenderWindow) && !IsDragWithOptionKey ( theDrag );
  630. }
  631.  
  632.  
  633.  
  634. static Boolean IsDragWithOptionKey ( DragReference theDrag )
  635. {
  636.     short    mouseDownModifiers, mouseUpModifiers;
  637.     
  638.     GetDragModifiers ( theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers );
  639.     return (mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey);
  640. }
  641.  
  642.  
  643.  
  644. void OutlineRegion ( RgnHandle theRgn )
  645. {
  646.     RgnHandle tempRgn;
  647.     
  648.     tempRgn = NewRgn ( );
  649.     CopyRgn ( theRgn, tempRgn );
  650.     InsetRgn ( tempRgn, 1, 1 );
  651.     DiffRgn ( theRgn, tempRgn, theRgn );
  652.     DisposeRgn ( tempRgn );
  653.     
  654.     return;
  655. }
  656.  
  657.  
  658.  
  659. OSErr DoWindowContentDrag ( WindowPtr theWindow, EventRecord* theEvent )
  660. {
  661.     OSErr            theErr = noErr;
  662.     DragReference    theDrag = (unsigned long) nil;
  663.     RgnHandle        dragRgn = nil;
  664.     Ptr                dataPtr = nil;
  665.     StScrpHandle    styleHandle = nil;
  666.     short            dataSize;
  667.     
  668.     DPtr            theDocument;
  669.     Rect            dragBounds;
  670.     ItemReference    theItem;
  671.     
  672.     
  673.     // create a new drag
  674.     theErr = NewDrag ( &theDrag );
  675.     if ( theErr ) goto CleanupAndBail;
  676.     
  677.     // use the window ptr as item reference for the heck of it
  678.     theItem = (ItemReference) theWindow;
  679.     
  680.     // add the data to the drag
  681.     theDocument = DPtrFromWindowPtr ( theWindow );
  682.     dataPtr = NewPtr ( 0L );
  683.     theErr = GetSelectedText ( theDocument, dataPtr, &dataSize );
  684.     if ( theErr || dataSize == 0 )    goto CleanupAndBail;
  685.     
  686.     theErr = AddDragItemFlavor ( theDrag, theItem, 'TEXT', dataPtr, dataSize, 0 );
  687.     if ( theErr )    goto CleanupAndBail;
  688.     DisposePtr ( dataPtr );
  689.     dataPtr = nil;
  690.     
  691.     
  692.     // Add style data
  693.     styleHandle = TEGetStyleScrapHandle ( theDocument->theText );
  694.     HLock ( (Handle) styleHandle );
  695.     AddDragItemFlavor ( theDrag, theItem, 'styl', (Ptr) *styleHandle, GetHandleSize ( (Handle) styleHandle ), 0 );
  696.     HUnlock ( (Handle) styleHandle );
  697.     DisposeHandle ( (Handle) styleHandle );
  698.     styleHandle = nil;
  699.     
  700.     
  701.     // generate the bounds and region for the drag using the window's
  702.     // content rectangle
  703.     dragBounds = (**((WindowPeek) theWindow)->contRgn).rgnBBox;
  704.     theErr = SetDragItemBounds(theDrag, theItem, &dragBounds);
  705.     if ( theErr ) goto CleanupAndBail;
  706.     
  707.     dragRgn = NewRgn ( );
  708.     GetSelectedTextRgn ( theDocument, dragRgn );
  709.     LocalRgnToGlobalRgn ( dragRgn, nil );
  710.     OutlineRegion ( dragRgn );
  711.     
  712.     // do the drag and clean up
  713.     TrackDrag ( theDrag, theEvent, dragRgn );
  714.     
  715.     
  716.     if ( DragIsNotInSourceWindow ( theDrag ) )
  717.     {
  718.         AEDesc    dropLocation;
  719.         
  720.         // Get the drop location
  721.         GetDropLocation ( theDrag, &dropLocation );
  722.         if ( !IsDragWithOptionKey ( theDrag ) && DropLocationIsFinderTrash ( &dropLocation) )
  723.         {
  724.             // Delete the exact text. Don't call DeleteTextSelection
  725.             TEDelete ( theDocument->theText );
  726.             theDocument->dirty = true;
  727.         }
  728.         
  729.         AEDisposeDesc ( &dropLocation );
  730.     }
  731.     
  732.     
  733. CleanupAndBail:
  734.     
  735.     if ( theDrag )
  736.         DisposeDrag(theDrag);
  737.     
  738.     if ( dragRgn )
  739.         DisposeRgn ( dragRgn );
  740.     
  741.     // These should be nil
  742.     if ( dataPtr )
  743.         DisposePtr ( dataPtr );
  744.     if ( styleHandle )
  745.         DisposeHandle ( (Handle) styleHandle );
  746.     
  747.     
  748.     return theErr;
  749. }
  750.  
  751.  
  752.  
  753. void LocalRgnToGlobalRgn ( RgnHandle theRgn, WindowRef theWindow )
  754. {
  755.     GrafPtr        savePort;
  756.     Point        localPt, globalPt;
  757.     
  758.     
  759.     if ( theWindow )
  760.     {
  761.         GetPort ( &savePort );
  762.         SetPortWindowPort ( theWindow );
  763.     }
  764.     
  765.     localPt = globalPt = *(Point*) &(*theRgn)->rgnBBox;        // top left
  766.     LocalToGlobal ( &globalPt );
  767.     SubPt ( localPt, &globalPt );
  768.     OffsetRgn ( theRgn, globalPt.h, globalPt.v );
  769.     
  770.     if ( theWindow )
  771.         SetPort ( savePort );
  772.         
  773.     return;
  774. }
  775.  
  776.  
  777.  
  778. void LocalRectToGlobalRect ( Rect* theRect, WindowRef theWindow )
  779. {
  780.     GrafPtr        savePort;
  781.     Point        localPt, globalPt;
  782.     
  783.     
  784.     if ( theWindow )
  785.     {
  786.         GetPort ( &savePort );
  787.         SetPortWindowPort ( theWindow );
  788.     }
  789.     
  790.     localPt = globalPt = *(Point*) theRect;        // top left
  791.     LocalToGlobal ( &globalPt );
  792.     SubPt ( localPt, &globalPt );
  793.     OffsetRect ( theRect, globalPt.h, globalPt.v );
  794.     
  795.     if ( theWindow )
  796.         SetPort ( savePort );
  797.         
  798.     return;
  799. }
  800.  
  801.  
  802.  
  803.  
  804. OSErr GetSelectedText ( DPtr theDocument, Ptr dataPtr, short* dataSize )
  805. {
  806.     OSErr theErr;
  807.     TEHandle teHandle = theDocument->theText;
  808.     
  809.     *dataSize = (**(teHandle)).selEnd - (**(teHandle)).selStart;
  810.     if ( *dataSize )
  811.     {
  812.         SetPtrSize ( dataPtr, *dataSize );
  813.         theErr = MemError ( );
  814.         if ( theErr )
  815.             return theErr;
  816.         BlockMoveData ( *(**(teHandle)).hText + (**(teHandle)).selStart, dataPtr, *dataSize );
  817.     }
  818.     
  819.     return noErr;
  820. }
  821.  
  822.  
  823.  
  824. void GetSelectedTextRgn ( DPtr theDocument, RgnHandle dataRgn )
  825. {
  826.     TEGetHiliteRgn ( dataRgn, theDocument->theText );
  827.     
  828.     return;
  829. }
  830.  
  831.  
  832.  
  833. //
  834. // Does the user want to drag something? First, checks the click could
  835. // be a drag, then waits to see if the user starts to drag the text.
  836. // 
  837. Boolean UserWantsToDrag ( WindowRef theWindow, Point globalPt )
  838. {
  839.     Point    localPt;
  840.     
  841.     localPt = globalPt;
  842.     GlobalToLocal ( &localPt );            // Assumes theWindow is current port
  843.     if ( PointInWindowSelection ( localPt, theWindow ) )
  844.         return WaitMouseMoved ( globalPt );
  845.         
  846.     return false;
  847. }
  848.  
  849.  
  850.  
  851. //
  852. // Returns true if the local point is in the window's current selection
  853. //
  854. Boolean PointInWindowSelection ( Point localPt, WindowRef theWindow )
  855. {
  856.     Boolean        bHit;
  857.     RgnHandle    tempRgn;
  858.     DPtr        theDocument;
  859.     
  860.     theDocument = DPtrFromWindowPtr ( theWindow );
  861.     if ( !(PtInDocument ( localPt, theDocument )) )
  862.         return false;
  863.     
  864.     tempRgn = NewRgn ( );
  865.     GetSelectedTextRgn ( theDocument, tempRgn );
  866.     bHit = PtInRgn ( localPt, tempRgn );
  867.     DisposeRgn ( tempRgn );
  868.     
  869.     return bHit;
  870. }
  871.  
  872.  
  873.  
  874. //
  875. //    Given a point in global coordinates, HitTest returns a pointer to a
  876. //    document structure if the point is inside a document window on the screen.
  877. //    If the point is not inside a document window, HitTest return NULL in
  878. //    theDoc. If the point is in a doument window and also in the viewRect of
  879. //    the document's TextEdit field, HitTest also returns the offset into
  880. //    the text that corresponds to that point. If the point is not in the text,
  881. //    HitTest returns -1.
  882. //
  883. short HitTest(Point theLoc, DPtr* theDoc)
  884. {
  885.     GrafPtr        savePort;
  886.     WindowPtr    theWindow;
  887.     short        offset;
  888.     
  889.     *theDoc = 0L;
  890.     offset = -1;
  891.     
  892.     if (FindWindow(theLoc, &theWindow) == inContent)
  893.     {
  894.         if ( Ours ( theWindow ) )
  895.         {
  896.             *theDoc = DPtrFromWindowPtr ( theWindow );
  897.             GetPort ( &savePort );
  898.             SetPort(theWindow);
  899.             GlobalToLocal(&theLoc);
  900.             SetPort ( savePort );
  901.             
  902.             if ((PtInRect(theLoc, &(**((**theDoc).theText)).viewRect)) && 
  903.                 (PtInRect(theLoc, &(**((**theDoc).theText)).destRect))) {
  904.                 
  905.                 offset = TEGetOffset(theLoc, (**theDoc).theText);
  906.  
  907.                 if ((TEIsFrontOfLine(offset, (**theDoc).theText)) && (offset) &&            
  908.                         ((*((**((**theDoc).theText)).hText))[offset - 1] != 0x0D) &&
  909.                         (TEGetPoint(offset - 1, (**theDoc).theText).h < theLoc.h)) {
  910.                     offset--;
  911.                 }
  912.             }
  913.         }
  914.     }
  915.  
  916.     return(offset);
  917. }
  918.  
  919.  
  920.  
  921. // TEIsFrontOfLine, given an offset and a TextEdit handle, returns true if
  922. // the given offset is at the beginning of a line start.
  923. short TEIsFrontOfLine ( short offset, TEHandle theTE )
  924.  
  925. {    short        line = 0;
  926.  
  927.     if ((**theTE).teLength == 0)
  928.         return(true);
  929.  
  930.     if (offset >= (**theTE).teLength)
  931.         return( (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0d );
  932.  
  933.     while ((**theTE).lineStarts[line] < offset)
  934.         line++;
  935.  
  936.     return( (**theTE).lineStarts[line] == offset );
  937. }
  938.  
  939.  
  940.  
  941. // TEGetLine, given an offset and a TextEdit handle, returns the line number
  942. // of the line that contains the offset.
  943. short TEGetLine ( short offset, TEHandle theTE )
  944.  
  945. {    short        line = 0;
  946.  
  947.     if (offset > (**theTE).teLength)
  948.         return((**theTE).nLines);
  949.  
  950.     while ((**theTE).lineStarts[line] < offset)
  951.         line++;
  952.     
  953.     return(line);
  954. }
  955.  
  956.  
  957.  
  958. //
  959. //    DrawCaret draws a caret in a TextEdit field at the given offset. DrawCaret
  960. //    expects the port to be set to the port that the TextEdit field is in.
  961. //    DrawCaret inverts the image of the caret onto the screen.
  962. //
  963. void DrawCaret(short offset, TEHandle theTE)
  964.  
  965. {    Point        theLoc;
  966.     short        theLine, lineHeight;
  967.  
  968.  
  969.     // Get the coordinates and the line of the offset to draw the caret.
  970.  
  971.     theLoc  = TEGetPoint(offset, theTE);
  972.     theLine = TEGetLine(offset, theTE);
  973.     
  974.     
  975.     // For some reason, TextEdit dosen't return the proper coordinates
  976.     // of the last offset in the field if the last character in the record
  977.     // is a carriage return. TEGetPoint returns a point that is one line
  978.     // higher than expected. The following code fixes this problem.
  979.     if ((offset == (**theTE).teLength) &&
  980.             (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0D) {
  981.         theLoc.v += TEGetHeight(theLine, theLine, theTE);
  982.     }
  983.  
  984.     // Always invert the caret when drawing.
  985.     PenMode(patXor);
  986.  
  987.     //Get the height of the line that the offset points to.
  988.     lineHeight = TEGetHeight(theLine, theLine, theTE);
  989.  
  990.     // Draw the appropriate caret image.
  991.     MoveTo(theLoc.h - 1, theLoc.v - 1);
  992.     Line(0, 1 - lineHeight);
  993.  
  994.     PenNormal();
  995. }
  996.  
  997.  
  998.  
  999. char GetCharAtOffset(short offset, TEHandle theTE)
  1000.  
  1001. {
  1002.     if (offset < 0)
  1003.         return(0x0D);
  1004.  
  1005.     return(((char *) *((**theTE).hText))[offset]);
  1006. }
  1007.  
  1008.  
  1009.  
  1010. Boolean WhiteSpace(char theChar)
  1011.  
  1012. {
  1013.     return((theChar == ' ') || (theChar == 0x0D));
  1014. }
  1015.  
  1016.  
  1017.  
  1018. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE)
  1019.  
  1020. {    char        theChar;
  1021.  
  1022.     if ((offset < 0) || (offset > (**theTE).teLength - 1))
  1023.         return(true);
  1024.  
  1025.     theChar = ((char *) *((**theTE).hText))[offset];
  1026.     return((theChar == ' ') || (theChar == 0x0D));
  1027. }
  1028.  
  1029.  
  1030.  
  1031. void InsertTextAtOffset(short offset, char *theBuf, long size, StScrpHandle theStyl, TEHandle theTE)
  1032.  
  1033. {
  1034.     if (size == 0)
  1035.         return;
  1036.  
  1037.     // If inserting at the end of a word and the selection does not begin with
  1038.     // a space, insert a space before the insertion.
  1039.     if (!WhiteSpaceAtOffset(offset - 1, theTE) &&
  1040.          WhiteSpaceAtOffset(offset, theTE) &&
  1041.         !WhiteSpace(theBuf[0])) {
  1042.  
  1043.         TESetSelect(offset, offset, theTE);
  1044.         TEKey(' ', theTE);
  1045.         offset++;
  1046.     }
  1047.  
  1048.     //    If inserting at the beginning of a word and the selection does not end
  1049.     //    with a space, insert a space after the insertion.
  1050.     if ( WhiteSpaceAtOffset(offset - 1, theTE) &&
  1051.         !WhiteSpaceAtOffset(offset, theTE) &&
  1052.         !WhiteSpace(theBuf[size - 1])) {
  1053.  
  1054.         TESetSelect(offset, offset, theTE);
  1055.         TEKey(' ', theTE);
  1056.     }
  1057.     
  1058.     TESetSelect(offset, offset, theTE);
  1059.     TEStyleInsert(theBuf, size, theStyl, theTE);
  1060.     TESetSelect(offset, offset + size, theTE);
  1061.     
  1062.     return;
  1063. }
  1064.  
  1065.  
  1066.  
  1067. //
  1068. // DropLocationIsFinderTrash returns true if the given dropLocation
  1069. // AEDesc is a descriptor of the Finder's Trash.
  1070. //
  1071. Boolean DropLocationIsFinderTrash ( AEDesc* dropLocation )
  1072.  
  1073. {    OSErr            result;
  1074.     AEDesc            dropSpec;
  1075.     FSSpecPtr        theSpec;
  1076.     CInfoPBRec        thePB;
  1077.     short            trashVRefNum;
  1078.     long            trashDirID;
  1079.     
  1080.     
  1081.     
  1082.     //    Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or
  1083.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  1084.     if ( (dropLocation->descriptorType != typeNull) &&
  1085.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr))
  1086.     {
  1087.  
  1088.         HLock(dropSpec.dataHandle);
  1089.         theSpec = (FSSpec *) *dropSpec.dataHandle;
  1090.  
  1091.         //    Get the directory ID of the given dropLocation object.
  1092.         thePB.dirInfo.ioCompletion = 0L;
  1093.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  1094.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  1095.         thePB.dirInfo.ioFDirIndex = 0;
  1096.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  1097.         
  1098.         result = PBGetCatInfo(&thePB, false);
  1099.  
  1100.         HUnlock(dropSpec.dataHandle);
  1101.         AEDisposeDesc(&dropSpec);
  1102.         
  1103.         if ( result )
  1104.             return(false);
  1105.  
  1106.         //    If the result is not a directory, it cannot be the Trash.
  1107.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  1108.             return(false);
  1109.  
  1110.         //    Get information about the Trash folder.
  1111.         FindFolder ( theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
  1112.  
  1113.         //    If the directory ID of the dropLocation object is the same as the directory ID
  1114.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  1115.         if ( thePB.dirInfo.ioDrDirID == trashDirID )
  1116.             return true;
  1117.     }
  1118.     
  1119.     
  1120.     return false;
  1121. }
  1122.  
  1123.